"""

.. module:: service_manager_service
.. moduleauthor:: @author: 

*Description*:

This module contains the ServiceManagerService class.

*Constants*:

.. seealso:: class :class:`runtime.service.Service`
"""

# Imports
#
import datetime
import threading
import time
import logging

from datastore.resource import *
from datastore.uri import *
from runtime.service import * 
from runtime.rt import Runtime
from runtime.node import LoggingLevels

# Constants
#

# Classes
#
class ServiceManagerService(Service):
    """
    **Constants**:
    """
    
    # This service is hard-coded to start in runtime.Runtime.run().

    def cb_start(self, uri, action, res):
        """
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        #self.logger.log(LoggingLevels.TRACE, "ServiceManagerService starting")
        self.logger.log(logging.INFO, "ServiceManagerService starting")
                        
        # services instances are stored here e.g.
        # http://node.internal/base/services/base/services/support_service
        self.instances = Uri('internal:base/services-list/')

        # watch (cb_config_update) for new services.
        self.spaces.set_watch(self.instances,
            self.cb_service_update, filter=['put', 'update'])

        # watch (cb_config_update) for the node to be stopped.
        #self.spaces.set_trace(Uri('internal:base/node_control'))
        self.spaces.set_watch(Uri('internal:base/node_control'),
            self.cb_node_control, filter=['put', 'update'])

    def cb_service_update(self, uri, action, res):
        """
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        self.logger.log(logging.DEBUG, "Starting Service (%s)",
            res.get_metadata(Uri('dc:title')))
        
        # load the service module
        if res.has_var('classname'):
            module_name, class_name = res.classname.rsplit('.', 1)
            self.logger.log(logging.DEBUG, "module %s class %s",
                            module_name, class_name)
            try:
                exec_arg =  "from %s import %s\n" + \
                            "%s(self.spaces, self.datastore, uri).start()\n"
                exec_val = exec_arg % (module_name, '*', class_name)
                #self.logger.log(logging.WARNING, "zzzzz\n%s", exec_val)
                exec(exec_val, locals())
            except ImportError:
                self.logger.log(logging.WARNING, "Import error (class %s): %s",
                    class_name, sys.exc_info()[1])
            except:
                self.logger.exception(
                   "Unable to load and/or start service class " + \
                   "(%s) text:\n%s\n error (%s)" % \
                   (class_name, exec_val, sys.exc_info()[1]))
        else:
            self.logger.log(logging.DEBUG, "module not loaded (%s)",
                            res.get_metadata(Uri('dc:title')))

    def cb_node_control(self, uri, action, res):
        """
        Expecting internal:base/node_control
        
        :param uri: Uri
        :param action:
        :param res: Resource
        """
        
        if res is not None:
            if res.has_var('action'):
                self.logger.log(logging.INFO, 
                    "Node shutdown (%s) initiated.", res.action)

                # Allow each service to do its own clean-up
                # This will not guarantee that daemon threads have been removed.
                services_uri_list = self.spaces.find(self.instances)
                for service_uri in services_uri_list:
                    self.spaces.delete(service_uri)

                # release the wait... 
                Runtime.node_control_semaphore.release()
            else:
                self.logger.log(logging.ERROR, 
                    "Node shutdown cancelled. No 'action' variable.")
        else:
            self.logger.log(logging.ERROR, "Node shutdown cancelled. res=None")
